﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

	<head>
		<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
		<title>Organization Unit Management - 组织单元管理</title>
		<link type="text/css" rel="stylesheet" href="../bootstrap.min.css" />
	</head>

	<body>

		<div class="document-contents">

			<h3>Introduction - 介绍</h3>
			<p>Organization units (OU) can be used to <strong>hierarchically group users and 
entities</strong>.</p>
			<p class="translation">组织单元（OU）可以被用来<strong>层次化分组用户及其实体</strong>。</p>
			<h3>OrganizationUnit Entity - OrganizationUnit 实体</h3>
			<p>An OU is represented by the <strong>OrganizationUnit</strong> 
	entity. Fundamental properties of this entity are;</p>
			<p class="translation">OU 是通过 <strong>OrganizationUnit</strong>实体来表示的。该实体的基本属性如下：</p>
			<ul>
				<li>
					<strong>TenantId</strong>: Tenant's Id of this OU. Can be null for 
		host OUs.</li>
				<p class="translation"><strong>TenantId</strong>：该 OU 的租户 Id。对于宿主 OU 可以为 null。</p>
				<li>
					<strong>ParentId</strong>: Parent OU's Id. Can be null if this is a 
		root OU.</li>
				<p class="translation"><strong>ParentId</strong>：父 OU 的 Id。如果是根 OU 可以为 null。</p>
				<li>
					<strong>Code</strong>: A hierarchical string code that is unique for 
		a tenant.</li>
				<p class="translation"><strong>Code</strong>：层次字符串码，对于每个租户都是唯一的。</p>
				<li>
					<strong>DisplayName</strong>: Shown name of the OU.</li>
				<p class="translation"><strong>DisplayName</strong>：OU 的显示名称。</p>
			</ul>
			<p>OrganizationUnit entitiy's primary key (Id) is <strong>long</strong> and 
	it derives from <a href="/Pages/Documents/Entities#DocAuditing">
					<strong>
	FullAuditedEntity</strong>
				</a> which provides audit information and 
	implements <a href="/Pages/Documents/Data-Filters#DocSoftDelete">
					<strong>
	ISoftDelete</strong>
				</a> interface (so, OUs are not deleted from database, 
	they are just marked as deleted).</p>
			<p class="translation">
				OrganizationUnit 实体的主键（Id）是 <strong>long</strong> 类型，
				OrganizationUnit 实体是从 <a href="../Entities.html#DocAuditing">FullAuditedEntity</a> 派生的，
				而且实现了 <a href="../Data-Filters.html#DocSoftDelete">ISoftDelete</a> 接口（因此，OU 是不能从数据库中真正删除的，只是标记删除）。
			</p>
			<h4>Organization Tree - 组织树</h4>
			<p>Since an OU can have a parent, all OUs of a tenant are in a <strong>tree</strong> 
	structure. There are some rules for this tree;</p>
			<p class="translation">
				因为一个 OU 可以拥有一个父结点，所以对于一个租户的所有 OU 构成了一个树形结构。对于这棵树有一些规则：
			</p>
			<ul>
				<li>There can be more than one root (which have null ParentId).</li>
				<p class="translation">可能不止拥有一个根（ParentId 为 null）。</p>
				<li>Maximum deep of tree is defined as a constant as OrganizationUnit.<strong>MaxDepth</strong>, 
		which is <strong>16</strong>.</li>
				<p class="translation">树的最大深度作为常量定义在 OrganizationUnit.<strong>MaxDepth</strong>，值为 16。</p>
				<li>There is a limit for first-level children count of an OU (because of 
		fixed 
		OU Code unit length explained below).</li>
				<p class="translation">OU 第一层子结点个数有限制（因为 OU 代码单元长度是固定的，在后续会解释）。</p>
			</ul>
			<h4>OU Code - OU 代码</h4>
			<p>OU code is automatically generated and maintained by OrganizationUnit 
	Manager. It's a string something like:</p>
			<p class="translation">OU 代码是被 OrganizationUnit 管理器自动生成和维护的。它是像下面一样的字符串：</p>
			<p>"<strong>00001.00042.00005</strong>"</p>
			<p>This code can be used to easily query database for all children 
	(recursively) of an OU. There are some rules for this code;</p>
			<p class="translation">
				这个代码用助于方便的查询数据库，获取一个 OU 的所有子结点（递归）。这个代码有一些规则：
			</p>
			<ul>
				<li>It's <strong>unique</strong> for a
					<a href="/Pages/Documents/Multi-Tenancy">tenant</a>.</li>
				<p class="translation">它对于<a href="../Multi-Tenancy.html">租户</a>来说是<strong>唯一</strong>的。</p>
				<li>All children of same OU have codes <strong>start with parent OU's 
		code</strong>.</li>
				<p class="translation">相同 OU 的子结点的代码都是以父 OU 的代码开始。</p>
				<li>It's <strong>fixed length</strong> based on level of OU in the tree, 
		as shown in the sample.</li>
				<p class="translation">基于 OU 在树中的级别它是固定长度的，如上面的示例。</p>
				<li>While OU code is unique, it can be <strong>changable</strong> if you 
		move an OU. So, we should reference an OU by Id, not Code.</li>
				<p class="translation">
					虽然 OU 代码是唯一的，但是如果你移动了一个 OU，那么它是<strong>可改变</strong>的。我们应该通过 Id 来引用 OU，而不是代码。
				</p>
			</ul>
			<h3>OrganizationUnit Manager - OrganizationUnit 管理器</h3>
			<p>
				<strong>OrganizationUnitManager</strong> class can be
				<a href="/Pages/Documents/Dependency-Injection">injected</a> and used to 
	manage OUs. Common use cases are:</p>
			<p class="translation">
				OrganizationUnitManager 类可以被注入并用来管理 OU。常见用例是：
			</p>
			<ul>
				<li>Create, Update or Delete an OU</li>
				<p class="translation">创建，更新或删除一个 OU。</p>
				<li>Move an OU in OU tree.</li>
				<p class="translation">在 OU 树中移动一个 OU。</p>
				<li>Getting information about OU tree and items.</li>
				<p class="translation">获取有关 OU 树和 OU 项的信息。</p>
			</ul>
			<h4>Multi Tenancy - 多租户</h4>
			<p>OrganizationUnitManager is designed to work for a <strong>single tenant</strong> in a 
time. It works for the <strong>current tenant</strong> as default.</p>
			<p class="translation">
				OrganizationUnitManager 被设计来工作在<strong>单租户</strong>环境。它以当前租户作为默认来进行工作。
			</p>
			<h3>Common Use Cases - 常见案例：</h3>
			<p>Here, we will see common use cases for OUs. You can find source code of 
	the samples 	<a href="https://github.com/aspnetboilerplate/aspnetboilerplate-samples/tree/master/OrganizationUnitsDemo" target="_blank">
	here</a>.</p>
			<p class="translation">
				在这里，我们将会看到 OU 的常见案例。
				你可以点击<a href="https://github.com/aspnetboilerplate/aspnetboilerplate-samples/tree/master/OrganizationUnitsDemo" target="_blank">这里</a>找到示例的源代码。
			</p>
			<h4>Creating Entity Belongs To An Organization Unit - 创建一个归属于组织单元的实体</h4>
			<p>Most obvious usage of OUs is to assign an entity to an OU. Let's see a 
	sample entity:</p>
			<p class="translation">
				OU 的最常见用法是分派一个实体归属于一个 OU。让我们看一个示例实体：
			</p>
			<pre lang="cs">public class Product : Entity, IMustHaveTenant, <strong>IMustHaveOrganizationUnit</strong>
{
    public virtual int TenantId { get; set; }

    <strong>public virtual long OrganizationUnitId { get; set; }</strong>
    
    public virtual string Name { get; set; }

    public virtual float Price { get; set; }
}</pre>
			<p>We simple created <strong>OrganizationUnitId</strong> property to assign 
	this entity to an OU. <strong>IMustHaveOrganizationUnit</strong> defines the OrganizationUnitId 
	property. We don't have to implement it, but it's suggested to provide 
	standardization. There is also an IMayHaveOrganizationId which has a <strong>
	nullable</strong> OrganizationUnitId property.</p>
			<p class="translation">
				我们的示例创建了 <strong>OrganizationUnitId</strong> 属性并分派这个实体到一个 OU。
				<strong>IMustHaveOrganizationUnit</strong> 定义了 OrganizationUnitId 属性。
				我们不必实现该接口，但是建议提供标准化。
				除此之外，还有一个 IMayHaveOrganizationId，该接口提供了一个<strong>可空的</strong> OrganizationUnitId 属性。
			</p>
			<p>Now, we can relate a Product to an OU and query products of a specific 
	OU.</p>
			<p class="translation">现在，我们可以将一个 Product 关联到一个 OU，并且查询一个特定 OU 的产品。</p>
			<p>
				<strong>Notice that</strong>; Product entity have a <strong>
	TenantId</strong> (which is a property of IMustHaveTenant) to distinguish 
	products of different tenants in a multi-tenant application (see
				<a href="/Pages/Documents/Multi-Tenancy#data-filters">multi tenancy document</a>). 
	If your application is not multi-tenant, you don't need this interface and 
	property.</p>
			<p class="translation">
				<strong>注意</strong>：Product 实体有 <strong>TenantId</strong>（它是 IMustHaveTenant 接口中定义的属性）属性来区分多租户应用程序中不同租户的产品（参阅<a href="/Pages/Documents/Multi-Tenancy#data-filters">多租户文档</a>）。
				如果你的应用程序不是多租户，那么你不需要这个接口及其属性。
			</p>
			<h4>Get Entities In An Organization Unit - 获取组织单元中的实体</h4>
			<p>Getting Products of an OU is simple. Let's see this sample
				<a href="/Pages/Documents/Domain-Services">domain service</a>:</p>
			<p class="translation">
				获取一个 OU 中的产品很简单。让我们看一下<a href="../Domain-Services.html">领域服务</a>的示例：
			</p>
			<pre lang="cs">public class ProductManager : IDomainService
{
    private readonly IRepository&lt;Product&gt; _productRepository;

    public ProductManager(IRepository&lt;Product&gt; productRepository)
    {
        _productRepository = productRepository;
    }

    <strong>public List&lt;Product&gt; GetProductsInOu(long organizationUnitId)
    {
        return _productRepository.GetAllList(p =&gt; p.OrganizationUnitId == organizationUnitId);
    }
				</strong>
}</pre>
			<p>We can simply write a predicate against Product.OrganizationUnitId as 
	shown above.</p>
			<p class="translation">我们可以对 Product.OrganizationUnitId 简单地写一个谓词，如上所示。</p>
			<h4>Get Entities In An Organization Unit Including It's Child Organization Units - 获取组织单元（及其子组织单元）中的实体</h4>
			<p>We may want to get Products of an organization unit including child 
	organization units. In this case, OU <strong>Code</strong> can help us:</p>
			<p class="translation">
				我们可能想获取一个组织单元及其子组织单元中的产品。在这种情况下，OU <strong>代码</strong>可以帮到我们：
			</p>
			<pre lang="cs">public class ProductManager : IDomainService
{
    private readonly IRepository&lt;Product&gt; _productRepository;
    private readonly IRepository&lt;OrganizationUnit, long&gt; _organizationUnitRepository;

    public ProductManager(
        IRepository&lt;Product&gt; productRepository, 
        IRepository&lt;OrganizationUnit, long&gt; organizationUnitRepository)
    {
        _productRepository = productRepository;
        _organizationUnitRepository = organizationUnitRepository;
    }

    <strong>[UnitOfWork]
    public virtual List&lt;Product&gt; GetProductsInOuIncludingChildren(long organizationUnitId)
    {
        var code = _organizationUnitRepository.Get(organizationUnitId).Code;

        var query =
            from product in _productRepository.GetAll()
            join organizationUnit in _organizationUnitRepository.GetAll() on product.OrganizationUnitId equals organizationUnit.Id
            where organizationUnit.Code.StartsWith(code)
            select product;

        return query.ToList();
    }</strong>
}</pre>
			<p>First, we got <strong>code</strong> of the given OU. Then we created a 
	LINQ with a <strong>join</strong> and <strong>StartsWith(code)</strong> condition (StartsWith 
	creates a <strong>LIKE</strong> query in SQL). Thus, we can hierarchically 
	get products of an OU.</p>
			<p class="translation">
				首先，我们获得给定 OU 的<strong>代码</strong>。
				然后，我们创建了一个带有 <strong>join</strong> 和 <strong>StartsWith(code)</strong> 条件（在 SQL 中 StartsWith 创建一个 <strong>LIKE</strong> 查询）的 LINQ。
				因此，我们可以层次化的获取一个 OU 的产品。
			</p>
			<h4>Filter Entities For A User - 为用户过滤实体</h4>
			<p>We may want to get all products those are in OUs of a specific user. 
	Example code:</p>
			<p class="translation">我们可能想获取特定用户所在 OU 中的所有产品。示例代码：</p>
			<pre lang="cs">public class ProductManager : IDomainService
{
    private readonly IRepository&lt;Product&gt; _productRepository;
    private readonly UserManager _userManager;

    public ProductManager(
        IRepository&lt;Product&gt; productRepository, 
        UserManager userManager)
    {
        _productRepository = productRepository;
        _organizationUnitRepository = organizationUnitRepository;
        _userManager = userManager;
    }

    <strong>public async Task&lt;List&lt;Product&gt;&gt; GetProductsForUserAsync(long userId)
    {
        var user = await _userManager.GetUserByIdAsync(userId);
        var organizationUnits = await _userManager.GetOrganizationUnitsAsync(user);
        var organizationUnitIds = organizationUnits.Select(ou =&gt; ou.Id);

        return await _productRepository.GetAllListAsync(p =&gt; organizationUnitIds.Contains(p.OrganizationUnitId));
    }</strong>
}</pre>
			<p>We simply found Ids of OUs of the user. Then used <strong>Contains</strong> 
	condition while getting products. Surely, we could create a LINQ query with join to 
	get the same list.</p>
			<p class="translation">
				我们简单的查询到该用户 OU 的 Id。
				然后使用 <strong>Contains</strong> 条件来得到产品。
				当然，我们可以创建一个带有 join 的 LINQ 查询来获得相同的列表。
			</p>
			<p>We may want to get products in user's OUs including their child OUs:</p>
			<p class="translation">我们可能想要获取用户所属 OU（并包含子 OU）产品：</p>
			<pre lang="cs">public class ProductManager : IDomainService
{
    private readonly IRepository&lt;Product&gt; _productRepository;
    private readonly IRepository&lt;OrganizationUnit, long&gt; _organizationUnitRepository;
    private readonly UserManager _userManager;

    public ProductManager(
        IRepository&lt;Product&gt; productRepository, 
        IRepository&lt;OrganizationUnit, long&gt; organizationUnitRepository, 
        UserManager userManager)
    {
        _productRepository = productRepository;
        _organizationUnitRepository = organizationUnitRepository;
        _userManager = userManager;
    }

    <strong>[UnitOfWork]
    public virtual async Task&lt;List&lt;Product&gt;&gt; GetProductsForUserIncludingChildOusAsync(long userId)
    {
        var user = await _userManager.GetUserByIdAsync(userId);
        var organizationUnits = await _userManager.GetOrganizationUnitsAsync(user);
        var organizationUnitCodes = organizationUnits.Select(ou =&gt; ou.Code);

        var query =
            from product in _productRepository.GetAll()
            join organizationUnit in _organizationUnitRepository.GetAll() on product.OrganizationUnitId equals organizationUnit.Id
            where organizationUnitCodes.Any(code =&gt; organizationUnit.Code.StartsWith(code))
            select product;

        return query.ToList();
    }</strong>
}</pre>
			<p>We combined <strong>Any</strong> with <strong>StartsWith</strong> 
	condition in a LINQ join statement.</p>
			<p class="translation">我们将带有 <strong>StartsWith</strong> 条件的 <strong>Any</strong> 联合到了一个 LINQ join 语句中。</p>
			<p>Surely, there may be much more complex requirements, but all can be done 
	with LINQ or SQL.</p>
			<p class="translation">当然，也可能有更复杂的需求，但是所有的需求都可以使用 LINQ 或 SQL 完成。</p>
			<h3>Settings - 设置</h3>
			<p>You can inject and use <strong>IOrganizationUnitSettings</strong> interface to get 
	Organization Units setting values. Currently, there is just a single setting 
	that can be changed for your application needs:</p>
			<p class="translation">
				你可以注入并使用 <strong>IOrganizationUnitSettings</strong> 接口来获取组织设置的设置值。
				当前，只有一个设置需要为你的应用程序进行修改：
			</p>
			<ul>
				<li>
					<strong>MaxUserMembershipCount</strong>: Maximum allowed membership 
		count for a user.<br />
		Default value is <strong>int.MaxValue</strong> which 
		allows a user to be member of unlimited OUs in same time.<br />
		Setting name is a constant defined in <em>
		AbpZeroSettingNames.OrganizationUnits.MaxUserMembershipCount</em>.</li>
				<p class="translation">
					<strong>MaxUserMembershipCount</strong>：一个用户最多允许成为 OU 成员的数量。
					默认值是 <strong>int.MaxValue</strong>，它允许用户同时成为无数 OU 的成员。
					设置名称是一个定义在 <em>AbpZeroSettingNames.OrganizationUnits.MaxUserMembershipCount</em> 中的常量。
				</p>
			</ul>
			<p>You can change setting values using
				<a href="/Pages/Documents/Setting-Management">setting manager</a>.</p>
			<p class="translation">
				你可以使用<a href="../Setting-Management.html">设置管理器</a>来修改设置值。
			</p>

		</div>

	</body>

</html>
